'use strict';

var utils = require('../utils');

/**
 * Config-specific merge-function which creates a new config-object
 * by merging two configuration objects together.
 *
 * @param {Object} config1
 * @param {Object} config2
 * @returns {Object} New object resulting from merging config2 to config1
 */
//合并两个配置
//注意优先保留第二个配置
module.exports = function mergeConfig(config1, config2) {
    // eslint-disable-next-line no-param-reassign
    config2 = config2 || {};
    var config = {};
    //比较关键的值
    var valueFromConfig2Keys = ['url', 'method', 'data'];
    //深层次的值，都为引用类型
    var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];
    // 用户可以手动设置的配置项
    var defaultToConfig2Keys = [
        'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
        'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
        'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',
        'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',
        'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'
    ];
    // 可以自定义的合法状态码
    var directMergeKeys = ['validateStatus'];
    //用来合并两个对象（会合并里面的每个值和属性）
    function getMergedValue(target, source) {
        // 递归遍历直到遍历出原生对象为止，深度合并
        if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
            return utils.merge(target, source);
        } else if (utils.isPlainObject(source)) {
            return utils.merge({}, source);
        } else if (utils.isArray(source)) {
            return source.slice();
        }
        return source;
    }
    //config2[prop]有值则以config2[prop]为准，否则以config1[prop]为准
    function mergeDeepProperties(prop) {
        // 如果config2中存在参数值
        if (!utils.isUndefined(config2[prop])) {
            config[prop] = getMergedValue(config1[prop], config2[prop]);
            // 如果参数一中存在这个值
        } else if (!utils.isUndefined(config1[prop])) {
            config[prop] = getMergedValue(undefined, config1[prop]);
        }
    }

    // 对于第一种属性，url、method和data不能从默认配置中取值，因此如果userConfig中有就直接取userConfig中的值。
    utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
        if (!utils.isUndefined(config2[prop])) {
            config[prop] = getMergedValue(undefined, config2[prop]);
        }
        //没有解析第一个config，因为这一组配置没有默认值
    });
    // 对于第二种属性，如果userConfig中有，就将其与defaultConfig进行合并。
    // 如果congfig2中存在这个值以config2为准，否则使用config1的值
    utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);
    // 对于第三种普通属性，如果congfig2有该属性值，直接添加进去，如果没有的话检验config1是否有该属性，有则添加进去
    utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
        if (!utils.isUndefined(config2[prop])) {
            // 这里第一个参数为undefined，本质上就是对一个属性进行添加
            config[prop] = getMergedValue(undefined, config2[prop]);
        } else if (!utils.isUndefined(config1[prop])) {
            config[prop] = getMergedValue(undefined, config1[prop]);
        }
    });

    utils.forEach(directMergeKeys, function merge(prop) {
        // 只有一个有效的stute码，优先第二个配置
        if (prop in config2) {
            // 如果设置合法状态码的话，进行合并（或者说与前面的配置进行相互补充，保证完整性）
            config[prop] = getMergedValue(config1[prop], config2[prop]);
            // 如果没有设置的话直接使用第一个配置
        } else if (prop in config1) {
            config[prop] = getMergedValue(undefined, config1[prop]);
        }
    });
    //连接各个配置项，axiosKeys是axios所有标准配置项
    var axiosKeys = valueFromConfig2Keys
        .concat(mergeDeepPropertiesKeys)
        .concat(defaultToConfig2Keys)
        .concat(directMergeKeys);

    //找出那些不在标准配置中的其他配置
    var otherKeys = Object
        .keys(config1)
        .concat(Object.keys(config2))
        .filter(function filterAxiosKeys(key) {
            return axiosKeys.indexOf(key) === -1;
        });
    //添加其他的非标准配置项，即用户的自定义配置
    utils.forEach(otherKeys, mergeDeepProperties);

    return config;
};